/*
 * Copyright (c) United Binary LLC.  All rights reserved.
 * 
 * This code is licensed under the MIT License
 * 
 * SEE: http://harnessit.codeplex.com/license
 * 
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Collections;


namespace UnitedBinary.HarnessIt.TestEngine
{
	/// <summary>
	/// Summary description for EngineEnumerator.
	/// </summary>
	public class EngineEnumerator
	{
		private string path = "";
		ITestEngine [] iEngines = new ITestEngine [0];

		public EngineEnumerator(string path)
		{
			this.path = path;
		}

		public void Enumerate()
		{
			ArrayList engines = new ArrayList();

			string [] files = Directory.GetFiles(path,"*.dll");
			foreach(string f in files)
			{
				try
				{
					ITestEngine [] filesEngines = GetEnginesFromAssembly(f);
					foreach(ITestEngine e in filesEngines)
					{
						engines.Add(e);
					}
				}
				catch(Exception e)
				{
					Debug.WriteLine("EngineEnumerator: Error reading execution engine. Details: " + e.ToString());
				}
			}

			Debug.WriteLine("Found " + engines.Count + " Test Engines in "+files.Length+" DLL files.");
			
			iEngines = new ITestEngine [engines.Count];
			for (int i=0; i<engines.Count; i++)
			{
				iEngines[i] = (ITestEngine)engines[i];
				Debug.WriteLine("Found engine: " + iEngines[i].Name);
			}
		}

		private ITestEngine [] GetEnginesFromAssembly(string file)
		{
			//
			// Load the assembly using reflection.
			//
			System.Reflection.Assembly A;
			A = System.Reflection.Assembly.LoadFrom(file);
			
			//
			// Iterate over the types in the assembly.  If they are classes
			// create test class objects and initialize them to these classes.
			//
			ArrayList engines = new ArrayList();
			Type [] types = A.GetTypes();
			foreach(Type t in types)
			{
				if (t.IsClass)
				{
					object [] atts = t.GetCustomAttributes(typeof(TestEngineAttribute),true);
					if (atts != null && atts.Length>0 && ClassImplementsITestEngine(t))
					{
						try
						{
							engines.Add(LoadEngineFromType(t));
						}
						catch {}
					}
				}
			}

			ITestEngine [] localEngines = new ITestEngine [engines.Count];
			for (int i=0; i<engines.Count; i++)
			{
				localEngines[i] = (ITestEngine)engines[i];
			}

			return localEngines;
		}

		public ITestEngine LoadEngineFromType(Type C)
		{
			if (!C.IsClass)
			{
				throw new ArgumentException("The type must be a class.","C");
			}

			return (ITestEngine)Activator.CreateInstance(C);
		}

		private bool ClassImplementsITestEngine(Type C)
		{
			if (!C.IsClass)
			{
				throw new ArgumentException("The type must be a class.","C");
			}

			bool interfaceFound = false;
			Type [] interfaces = C.GetInterfaces();
			foreach(Type i in interfaces)
			{
				string typeString = i.FullName;
				string itester = typeof(ITestEngine).ToString();

				if (typeString == itester)
				{
					interfaceFound = true;
				}
			}

			return interfaceFound;
		}

		public ITestEngine [] AvailableEngines
		{
			get
			{
				return iEngines;
			}
		}

		public ITestEngine GetEngine(string classname)
		{
			foreach(ITestEngine i in AvailableEngines)
			{
				if (classname.ToLower() == i.GetType().FullName.ToLower())
				{
					return i;
				}
			}

			throw new ArgumentException("Test Engine specified by " + classname + " was not found.","name");
		}

		public ITestEngine GetEngine(Type type)
		{
			foreach(ITestEngine i in AvailableEngines)
			{
				if (type.FullName == i.GetType().FullName)
				{
					return i;
				}
			}

			throw new ArgumentException("Test Engine specified by Type " + type + " was not found.","type");
		}
	}
}
